"""告警相关"""
import arrow
import random
import datetime
import json

import bson
from bson import ObjectId
from flask import request, jsonify, g, session, current_app as app
from app.auth import permission_required
from app.db import Pagination
from app.db.models.alert_groups import AlertGroup, AlertEnableDetail
from app.db.models.alertreceivers import AlertReceivers
from app.db.models.alerts import Alert
from app.db.models.balances import Balances
from app.db.models.notificationsendlogs import NotificationSendLog
from app.db.models.jobs import Job
from app.errors import ParameterError, AddAlertGroupError, PackageLimit
from app.handler.v2_alert import CodeVerified
from app.libs.redprint import RedPrint
from app.libs.enums import TaskType, AVAILABLE_TASK_GROUP_TUPLE, WS_TASK_GROUP_TUPLE
from app.libs.utils import page_info
from app.libs.cas_api import request_cas
from app.libs.sendcloud import SendCloud
from app.libs.yunpian import YunPian
from app.log.logs import (
    CAlertStrategyLog,
    DAlertStrategyLog,
    UAlertStrategyLog,
    CAlertReceiversLog,
    UAlertReceiversLog,
    DAlertReceiversLog
)
from app.validators.alerts import (
    AlertListVerification,
    AlertGroupForm,
    AlertGroupSettingsForm,
    AlertReceiversListForm,
    AddReceiverForm,
    EditReceiverForm,
    DeleteReceiverForm,
    ReceiverVerifiedForm,
)
from app.validators.notificationsendlogs import NotificationSendLogListVerification

api = RedPrint('alerts')


def deal_alert_detail_list(alert_list):
    result = []
    for alert in alert_list:
        _alert = alert.to_mongo().to_dict()
        if 'taskId' in _alert['detail']:
            _alert['detail']['taskId'] = str(_alert['detail']['taskId'])

        _alert['user'] = None
        if 'username' in alert.uid:
            _alert['user'] = {'username': alert.uid['username']}

        try:
            _alert['job'] = {'note': alert.jobId['note']}
            data = _alert['detail'].get('data', {})
            if data:
                data.update({"is_checked": True if alert.jobId['securityStatus'].get("update_time", "") else False})
        except Exception as e:
            _alert['job'] = None

        try:
            _alert['task'] = {
                'taskSettings': alert.taskId['taskSettings'],
                'alertSettings': alert.taskId['alertSettings'],
                '_id': str(alert.taskId.pk)}
        except Exception as e:
            _alert['task'] = None

        if not _alert['detail'].get("data"):
            _alert['task'] = None

        _alert['id'] = str(_alert.pop('_id'))
        _alert.pop('jobId')
        _alert.pop('refId')
        _alert.pop('taskId')
        _alert.pop('uid')

        result.append(_alert)

    return result


@api.route('/detail_list', methods=['GET'])
def alert_detail_list():
    user_id = ''
    if g.role.name in ['普通用户']:
        user_id = g.user.id

    form = AlertListVerification()
    query_dict = form.get_query_dict()

    page = query_dict.pop('page')
    limit = query_dict.pop('limit')

    if user_id:
        query_dict['uid'] = user_id

    alerts = Alert.objects.find(query_dict).order_by('-createTime').paginate(page, limit)
    items = deal_alert_detail_list(alerts.items)

    ctx = {
        'code': 200,
        'msg': 'ok',
        'data': {
            'count': alerts.total,
            'items': items,
            'pageInfo': {
                'currentPage': page,
                'hasNextPage': True if 0 < page < alerts.pages else False,
                'hasPreviousPage': True if page > 1 else False,
                'itemCount': len(items),
                'pageCount': alerts.pages,
                'perPage': limit,
            },
        }
    }

    return jsonify(ctx)


def get_alert_today_top_asset(user_id, start_time):
    if user_id:
        match_query = {"$match": {"uid": user_id, "createTime": {"$gte": start_time}}}
    else:
        match_query = {"$match": {"createTime": {"$gte": start_time}}}

    pipeline = [
        match_query,
        {"$project": {"target": 1, "jobId": 1}},
        {"$group": {"_id": {"target": "$target", "jobId": "$jobId"}, "count": {"$sum": 1}}},
        {"$sort": {"count": -1}},
    ]

    _alert_today_top_asset = Alert.objects().aggregate(pipeline)

    result = dict()
    is_ok = False
    for item in _alert_today_top_asset:
        result['asset'] = item['_id']['target']
        result['count'] = item['count']

        jobs = Job.objects.filter(pk=item["_id"]['jobId'])
        try:
            if jobs:
                is_ok = True
                result['note'] = jobs[0]['note']
            else:
                continue
        except Exception:
            continue
        break

    if not is_ok:
        result = dict()

    return result


def get_alert_count(user_id='', start_time=None):
    query_dict = {"taskType": {"$in": list(AVAILABLE_TASK_GROUP_TUPLE + WS_TASK_GROUP_TUPLE + (TaskType.asset.value, ))}}
    if user_id:
        query_dict['uid'] = user_id
    if start_time:
        query_dict['createTime'] = {"$gte": start_time}

    match_query = {"$match": query_dict}
    pipeline = [
        match_query,
        {"$project": {"taskType": 1}},
        {"$group": {"_id": {"taskType": "$taskType"}, "count": {"$sum": 1}}},
        {"$sort": {"count": -1}},
    ]
    alert_total = Alert.objects().aggregate(pipeline)
    return [item for item in alert_total]


def get_alert_count_of_task_type(user_id, task_type):
    query_dict = dict()
    query_dict['taskType'] = task_type
    if user_id:
        query_dict['uid'] = user_id
    return Alert.objects.filter(**query_dict).count()


def get_alert_count_of_task_category(user_id, task_category):
    query_dict = dict()
    query_dict['taskCategory'] = task_category
    if user_id:
        query_dict['uid'] = user_id
    return Alert.objects.filter(**query_dict).count()


@api.route('/detail_summary', methods=['GET'])
def alert_detail_summary():
    user_id = ''
    if g.role.name in ['普通用户']:
        user_id = g.user.id

    today_date = datetime.datetime.utcnow().date() - datetime.timedelta(days=1)
    start_time = datetime.datetime.utcnow().replace(
        year=today_date.year, month=today_date.month, day=today_date.day, hour=16, minute=0, second=0)

    data = {
        'AlertTodayTopAsset': 0,
        'aviliableCount': 0,
        'aviliableTodayCount': 0,
        'todayCount': 0,
        'totalCount': 0,
        **{f'{t}Count': 0 for t in WS_TASK_GROUP_TUPLE + (TaskType.asset.value, )},
        **{f'{t}TodayCount': 0 for t in WS_TASK_GROUP_TUPLE + (TaskType.asset.value, )},
    }

    alert_count = get_alert_count(user_id, start_time)
    for item in alert_count:
        if item["_id"]["taskType"] in AVAILABLE_TASK_GROUP_TUPLE:
            data["aviliableTodayCount"] = item["count"]
        else:
            data[f'{item["_id"]["taskType"]}TodayCount'] = item['count']
        data['todayCount'] += item['count']

    for t in WS_TASK_GROUP_TUPLE + (TaskType.asset.value, ):
        t_count = get_alert_count_of_task_type(user_id, t)
        data[f'{t}Count'] = t_count
        data['totalCount'] += t_count

    data['aviliableCount'] = get_alert_count_of_task_category(user_id, 'aviliable')
    data['totalCount'] += data['aviliableCount']

    data['AlertTodayTopAsset'] = get_alert_today_top_asset(user_id, start_time)
    ctx = {
        'code': 200,
        'msg': 'ok',
        'data': data
    }
    return jsonify(ctx)


@api.route('/notification_send_log_list', methods=['GET'])
@permission_required('admin')
def notification_send_log_list():
    form = NotificationSendLogListVerification()
    query_dict = form.get_query_dict()

    page = query_dict.pop('page')
    limit = query_dict.pop('limit')

    send_logs = NotificationSendLog.objects.find(query_dict).order_by('-id').paginate(page, limit)

    items = []
    for send_log in send_logs.items:
        _send_log = send_log.to_mongo().to_dict()
        try:
            _send_log['user'] = {'username': send_log.uid['username']}
        except Exception as e:
            _send_log['user'] = None

        receiver_list = []
        for receiver in send_log['receivers']:
            receiver.pop('_id')
            receiver_list.append(receiver)

        _send_log['_id'] = str(send_log.pk)
        _send_log['receivers'] = receiver_list
        _send_log['refId'] = str(send_log.refId)
        _send_log['uid'] = str(_send_log['uid'])

        items.append(_send_log)

    ctx = {
        'code': 200,
        'msg': 'ok',
        'data': {
            'count': send_logs.total,
            'items': items,
            'pageInfo': {
                'currentPage': page,
                'itemCount': len(items),
                'pageCount': send_logs.pages,
                'perPage': limit,
                'hasNextPage': True if 0 < page < send_logs.pages else False,
                'hasPreviousPage': True if page > 1 else False,
            },
        }
    }

    return jsonify(ctx)


@api.route('/notification_send_log_count', methods=['GET'])
@permission_required('admin')
def notification_send_log_count():
    request_args = request.args
    status = request_args.get('status', '')

    query_dict = dict()
    if status:
        query_dict['status'] = status

    ctx = {
        'code': 200,
        'msg': 'ok',
        'data': [],
    }

    if query_dict:
        count = NotificationSendLog.objects.filter(**query_dict).count()
        ctx = {
            'data': [{
                'NotificationSendLogCount': count,
                'status': status,
            }],
        }
    else:
        pipeline = [
            {"$project": {"status": 1}},
            {"$group": {"_id": {"status": "$status"}, "count": {"$sum": 1}}},
            {"$sort": {"status": -1}},
        ]
        count_list = NotificationSendLog.objects().aggregate(pipeline)

        for item in count_list:
            ctx['data'].append({
                'NotificationSendLogCount': item['count'],
                'status': item['_id']['status'],
            })

    return jsonify(ctx)


def get_alert_strategy_by_target_url(uid, target_url, source_ip):
    match = {'uid': uid, 'alertSettings.alertGroupIds.0': {"$exists": True}}
    if target_url:
        match['targetUrl'] = {'$regex': target_url}
    if source_ip:
        match['sourceIp'] = {'$regex': source_ip}

    aggregate = [
        {'$match': match},
        {'$group': {'_id': '$alertSettings.alertGroupIds', 'count': {'$sum': 1}}},
    ]
    jobs = Job.objects.aggregate(aggregate)

    alert_group_id_set = set()
    for job in jobs:
        alert_group_id_set.add(ObjectId(job['_id'][0]))

    result = AlertGroup.objects.filter(uid=uid, id__in=list(alert_group_id_set)).distinct('_id')

    return result


def get_job_maps(uid, alert_group_id_list):
    aggregate = [
        {'$match': {'uid': uid, 'verification.isVerified': True, 'alertSettings.alertGroupIds.0': {"$in": alert_group_id_list}}},
        {'$group': {'_id': '$alertSettings.alertGroupIds', 'count': {'$sum': 1}}},
    ]
    jobs = Job.objects.aggregate(aggregate)

    job_maps = dict()
    for job in jobs:
        job_maps[job['_id'][0]] = job['count']

    return job_maps


def get_receiver_maps(receiver_id_list):
    receiver_maps = dict()
    alert_receivers = AlertReceivers.objects.find({"_id": {"$in": receiver_id_list}})
    for alert_receiver in alert_receivers:
        receiver_maps[alert_receiver.id] = {
            'id': str(alert_receiver.id),
            'name': alert_receiver.name,
            'email': alert_receiver.email if alert_receiver.email else '--',
            'phone': alert_receiver.phone if alert_receiver.phone else '--',
        }
    return receiver_maps


@api.route('/alert_strategy_list', methods=['GET'])
def alert_strategy_list():
    form = AlertGroupForm().validate_for_api()
    page = form.data.get('page')
    count = form.get_count()

    target_url = form.data.get("target_url")
    name = form.data.get("name")
    source_ip = form.data.get("source_ip")

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    query_dict = {"uid": uid}

    if target_url or source_ip:
        alert_group_id_list = get_alert_strategy_by_target_url(uid, target_url, source_ip)
        query_dict["pk__in"] = alert_group_id_list

    if name:
        query_dict["name"] = {'$regex': name}

    data = AlertGroup.objects.filter(**query_dict)
    total = data.count()
    if (page - 1) * count >= total:
        return jsonify({'code': 200, "status": "success", 'data': {'results': [], 'count': total, "uid": str(uid),
                                                               "pageInfo": page_info(page, count, total)}})
    data = data.paginate(page, count)

    receiver_id_list = []
    alert_group_id_list = []
    results = []
    for item in data.items:
        alert_group_id_list.append(str(item.id))
        receiver_id_list.extend(item.receiverIds)
        enable_detail = item.alertEnableDetail
        alert_settings = item.alertSettings.to_dict()
        notification = form.get_notification_list(alert_settings)
        _d = {
            "id": str(item.id),
            "uid": str(item.uid),
            "enable": item.enable,
            "name": item.name,
            "is_default": item.isDefault,
            "receiverIds": item.receiverIds,
            "notification": notification,
            "enable_detail": {}
        }
        if enable_detail:
            _d.update({"enable_detail": enable_detail.to_mongo().to_dict()})
        results.append(_d)

    job_maps = get_job_maps(uid, alert_group_id_list)

    receiver_maps = dict()
    if receiver_id_list:
        receiver_maps = get_receiver_maps(list(set(receiver_id_list)))

    for item in results:
        item['alert_receivers'] = [receiver_maps.get(receiver_id) for receiver_id in item.get('receiverIds', [])
                                   if receiver_maps.get(receiver_id)]
        item['job_count'] = job_maps.get(item.get('id'), 0)
        item.pop('receiverIds', [])

    return jsonify({'code': 200, "status": "success", 'data': {'results': results, 'count': total, "uid": str(uid),
                                                               "pageInfo": page_info(page, count, total)}})


@api.route('/alert_strategy_detail', methods=['GET'])
def alert_strategy_detail():
    form = AlertGroupForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    alert_group = form.check_alert_group_exist(uid)

    jobs = Job.objects.find({'alertSettings.alertGroupIds.0': str(alert_group.id)})
    job_ids = [str(job.id) for job in jobs]

    alert_settings = alert_group.alertSettings.to_dict()
    enable_detail = alert_group.alertEnableDetail
    default_allowed_time = {'start': '00:00:00', 'end': '23:59:59'}

    result = {
        "id": alert_group.id,
        "include_wechat": alert_group.includeWechat,
        "name": alert_group.name,
        "receivers": alert_group.receiverIds,
        "job_ids": job_ids,
        "notification": form.get_notification_list(alert_settings),
        "enable": form.get_enable_list(alert_settings),
        "sms_allowed_time": alert_settings.get("smsAllowedTime", default_allowed_time),
        "email_allowed_time": alert_settings.get("emailAllowedTime", default_allowed_time),
        "email_daily_limit": alert_settings.get("emailDailyLimit", 10),
        "sms_daily_limit": alert_settings.get("smsDailyLimit", 10),
        "enable_detail": {}
    }
    if enable_detail:
        result.update({"enable_detail": enable_detail.to_mongo().to_dict()})

    return jsonify({'code': 200, "status": "success", 'data': result})


@api.route('/add_alert_strategy', methods=['POST'])
def add_alert_strategy():
    form = AlertGroupSettingsForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    form.check_alert_group_name_exist(uid=uid)
    form.check_receivers_exist()

    if balance := Balances.objects.filter(uid=uid).first():
        if balance.balance.alertGroup.used >= balance.balance.alertGroup.total:
            raise PackageLimit(msg='您还可以添加0个策略')
    else:
        raise AddAlertGroupError(msg='请先升级套餐')

    alert_group = AlertGroup()
    alert_group.isDefault = False
    alert_group.includeWechat = False
    alert_group.enable = True
    alert_group.uid = uid
    alert_group.name = form.name
    alert_group.receiverIds = form.receivers
    alert_group.alertSettings.enable = form.enable
    alert_group.alertSettings.notification = form.notification
    alert_group.alertSettings.smsAllowedTime = form.sms_allowed_time
    alert_group.alertSettings.emailAllowedTime = form.email_allowed_time
    alert_group.alertSettings.smsDailyLimit = form.sms_daily_limit
    alert_group.alertSettings.emailDailyLimit = form.email_daily_limit
    alert_group.alertEnableDetail = AlertEnableDetail.from_dict(form.enable_detail.data)
    alert_group.save()

    # 更新资产告警id
    job_ids = form.job_ids
    Job.objects(pk__in=job_ids, uid=uid, verification__isVerified=True).update(
        alertSettings__alertGroupIds=[str(alert_group.id)])

    Balances.objects(uid=uid).update_one(inc__balance__alertGroup__used=1)

    log_info = {
        'affects': [],
        'actionMessage': '新增告警策略',
        'detail': f'新增告警策略, 策略名称: {form.name}'
    }
    CAlertStrategyLog(**log_info).info(log_info['actionMessage'], log_info['detail'])

    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


@api.route('/edit_alert_strategy', methods=['POST'])
def edit_alert_strategy():
    form = AlertGroupSettingsForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    alert_group = form.check_alert_group_exist(uid)

    ori_alert_group_name = alert_group.name
    alert_settings = alert_group.alertSettings.to_dict()
    enable_list = form.get_enable_list(alert_settings)
    notification_list = form.get_notification_list(alert_settings)
    job_count = Job.objects.find({"alertSettings.alertGroupIds.0": str(alert_group.pk)}).count()
    ori_settings = deal_alert_group_log(ori_alert_group_name, enable_list, notification_list,
                                        len(alert_group.receiverIds), job_count, alert_settings)

    if not alert_group.isDefault and alert_group.name != form.name:
        form.check_alert_group_name_exist(uid=uid)
        alert_group.name = form.name

    form.check_receivers_exist()

    alert_group.receiverIds = form.receivers
    alert_group.alertSettings.enable = form.enable
    alert_group.alertSettings.notification = form.notification
    alert_group.alertSettings.smsAllowedTime = form.sms_allowed_time
    alert_group.alertSettings.emailAllowedTime = form.email_allowed_time
    alert_group.alertSettings.smsDailyLimit = form.sms_daily_limit
    alert_group.alertSettings.emailDailyLimit = form.email_daily_limit
    alert_group.alertEnableDetail = AlertEnableDetail.from_dict(form.enable_detail.data)
    alert_group.save()
    alert_group.reload()
    # 查询历史的告警资产
    jobs = Job.objects.find({"alertSettings.alertGroupIds.0": str(alert_group.id)}).distinct("_id")
    remove_jobs = set(jobs) - set(form.job_ids)

    # 更新资产告警id
    job_ids = form.job_ids
    Job.objects(pk__in=job_ids, uid=uid, verification__isVerified=True).update(alertSettings__alertGroupIds=[str(alert_group.id)])
    Job.objects(pk__in=list(remove_jobs), uid=uid).update(alertSettings__alertGroupIds=[])

    alert_settings = alert_group.alertSettings.to_dict()
    enable_list = form.get_enable_list(alert_settings)
    notification_list = form.get_notification_list(alert_settings)
    job_count = len(job_ids)
    edit_settings = deal_alert_group_log(alert_group.name, enable_list, notification_list,
                                         len(alert_group.receiverIds), job_count, alert_settings)

    log_info = {
        'affects': [],
        'actionMessage': '编辑告警策略',
        'detail': f'编辑告警策略, 修改前配置： {ori_settings}, 修改后配置： {edit_settings}'
    }
    UAlertStrategyLog(**log_info).info(log_info['actionMessage'], log_info['detail'])

    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


def deal_alert_group_log(alert_group_name, enable, notification, receiver_count, job_count, alert_settings):
    notification_maps = {"email": "邮件", "sms": "短信", "wechat": "微信", "in_box": "站内信"}
    enable_maps = {
        "vul": "Web漏洞监测",
        "ssl": "SSL安全监测",
        "asset": "资产变动监测",
        "securityEvent": "安全事件监测",
        "content": "内容违规监测",
        "http": "HTTP监测",
        "ping": "Ping监测",
        "hostVul": "主机安全监测",
        TaskType.change_check.value: "篡改监测",
    }

    sms_allowed_time = alert_settings.get("smsAllowedTime", {})
    email_allowed_time = alert_settings.get("emailAllowedTime", {})
    log_detail = json.dumps({
        u"策略名称": alert_group_name,
        u"告警人": f"{receiver_count}个",
        u"告警资产": f"{job_count}个",
        u"通知类型": ",".join([notification_maps.get(item) for item in notification if notification_maps.get(item)]),
        u"告警类型": ",".join([enable_maps.get(item) for item in enable if enable_maps.get(item)]),
        u"短信生效时间": "{}~{}".format(sms_allowed_time.get("start", "00:00:00"), sms_allowed_time.get("end", "23:59:59")),
        u"邮件生效时间": "{}~{}".format(email_allowed_time.get("start", "00:00:00"), email_allowed_time.get("end", "23:59:59")),
        u"每日短信告警上限": alert_settings.get("smsDailyLimit", 10),
        u"每日邮件告警上限": alert_settings.get("emailDailyLimit", 10),
    }, ensure_ascii=False)
    return log_detail


@api.route('/delete_alert_strategy', methods=['POST'])
def delete_alert_strategy():
    form = AlertGroupForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    alert_group_ids = form.alert_group_ids
    if not alert_group_ids:
        raise ParameterError(msg='请先选择需要删除的告警策略')

    alert_groups = AlertGroup.objects.filter(pk__in=alert_group_ids, uid=uid, isDefault=False)
    if not alert_groups:
        raise ParameterError(msg='策略不存在或已经删除')

    alert_group_ids = []
    alert_group_names = []
    for alert_group in alert_groups:
        alert_group_ids.append(str(alert_group.pk))
        alert_group_names.append(alert_group.name)

    detail = ",".join(alert_group_names)
    alert_group_count = len(alert_group_names)

    AlertGroup.objects.filter(pk__in=alert_group_ids, uid=uid, isDefault=False).delete()
    Job.objects(alertSettings__alertGroupIds__in=alert_group_ids, uid=uid).update(alertSettings__alertGroupIds=[])

    if alert_group_count > 0:
        Balances.objects(uid=uid).update_one(inc__balance__alertGroup__used=-alert_group_count)

    log_info = {
        'affects': [],
        'actionMessage': '删除告警策略',
        'detail': f'删除告警策略, 策略名称: {detail}'
    }
    DAlertStrategyLog(**log_info).info(log_info['actionMessage'], log_info['detail'])

    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


@api.route('/alert_strategy_switch', methods=['POST'])
def alert_strategy_switch():
    form = AlertGroupForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    switch = form.data.get("switch", False)
    alert_group = form.check_alert_group_exist(uid)
    alert_group.enable = switch
    alert_group.save()

    log_info = {
        'affects': [],
        'actionMessage': '编辑告警策略',
        'detail': f'编辑告警策略, 策略名称: {alert_group.name} 策略开关: {"开启" if switch else "关闭"}'
    }
    UAlertStrategyLog(**log_info).info(log_info['actionMessage'], log_info['detail'])

    return jsonify({'code': 200, 'message': '操作成功', 'status': 'success'})


@api.route('/alert_strategy_jobs', methods=['GET'])
def alert_strategy_jobs():
    form = AlertGroupForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    alert_group = form.check_alert_group_exist(uid)
    jobs = Job.objects.find({'alertSettings.alertGroupIds.0': str(alert_group.id)})
    job_list = [{"id": str(job.id), "target_url": job.targetUrl, "note": job.note, "source_ip": "" if job.assetType == 'host' else job.sourceIp} for job in
                jobs]

    return jsonify({'code': 200, "status": "success", 'data': job_list})


@api.route('/alert_strategy_jobs_comp', methods=['POST'])
def alert_strategy_jobs_comp():
    form = AlertGroupForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    new_job_ids = form.job_ids
    if not new_job_ids:
        raise ParameterError(msg='请先选择资产')

    if form.alert_group_id:
        alert_group = form.check_alert_group_exist(uid)
        jobs = Job.objects.find({'verification.isVerified': True, 'alertSettings.alertGroupIds.0': str(alert_group.id)})
        remove_job_maps = {job.id: {"id": str(job.id), "target_url": job.targetUrl, "note": job.note,
                                    "source_ip": "" if job.assetType == 'host' else job.sourceIp, "type": "remove"} for job in jobs if job.id not in new_job_ids}

        jobs = Job.objects.find({"_id": {"$in": new_job_ids}, 'verification.isVerified': True,
                                 'alertSettings.alertGroupIds.0': {"$ne": str(alert_group.id)}})
        add_job_maps = {job.id: {"id": str(job.id), "target_url": job.targetUrl, "note": job.note,
                                 "source_ip": "" if job.assetType == 'host' else job.sourceIp, "type": "add"} for job in jobs}
    else:
        remove_job_maps = {}

        jobs = Job.objects.find({"_id": {"$in": new_job_ids}, 'verification.isVerified': True,
                                 'alertSettings.alertGroupIds.0': {"$exists": True}})
        add_job_maps = {job.id: {"id": str(job.id), "target_url": job.targetUrl, "note": job.note,
                                 "source_ip": "" if job.assetType == 'host' else job.sourceIp, "type": "add"} for job in jobs}

    result = []
    new_job_ids = set(new_job_ids)
    for job_id, job_detail in remove_job_maps.items():
        if job_id not in new_job_ids:
            result.append(job_detail)
    for job_id, job_detail in add_job_maps.items():
        result.append(job_detail)

    return jsonify({'code': 200, "status": "success", 'data': result})


@api.route('/alert_strategy_receivers', methods=['GET'])
def alert_strategy_receivers():
    form = AlertGroupForm().validate_for_api()

    uid = g.user.id
    if g.role.name == '管理员':
        uid = form.uid

    receivers = AlertReceivers.objects.filter(uid=uid)
    result = []
    for receiver in receivers:
        result.append({
            "id": str(receiver.id),
            "phone_verified": receiver.phoneVerified,
            "email_verified": receiver.emailVerified,
            "enable": receiver.enable,
            "is_default": receiver.isDefault,
            "name": receiver.name,
        })
    return jsonify({'code': 200, "status": "success", 'data': result})


@api.route('/user/full', methods=['GET'])
@permission_required('admin')
def get_user_all_alart():
    """鲁班后台用户查询所有的告警策略和联系人设置"""
    uid = request.args.get("uid", "")
    if not uid:
        raise ParameterError(msg="用户ID错误")
    try:
        uid = bson.ObjectId(uid)
    except bson.errors.BSONError:
        raise ParameterError(msg="用户ID错误")
    receivers = []
    for receiver in AlertReceivers.objects.filter(uid=uid):
        receivers.append({
            "id": str(receiver.pk),
            "email": receiver.email or "",
            "email_verified": receiver.emailVerified,
            "enable": receiver.enable,
            "is_default": receiver.isDefault,
            "name": receiver.name,
            "phone": receiver.phone or "",
            "phone_verified": receiver.phoneVerified,
        })
    alert_group_id_list = []
    strategies = []
    for item in AlertGroup.objects.filter(uid=uid):
        alert_group_id_list.append(item.id)
        notification = []
        for k, v in item.alertSettings.to_dict().get("notification", {}).items():
            if v:
                notification.append(k)
        strategies.append({
            "id": str(item.id),
            "enable": item.enable,
            "name": item.name,
            "is_default": item.isDefault,
            "receiver_count": len(item.receiverIds) if item.receiverIds else 0,
            "notification": notification
        })
    job_maps = get_job_maps(uid, alert_group_id_list)
    for item in strategies:
        item['job_count'] = job_maps.get(item.get('id'), 0)
    return jsonify({'code': 200, 'msg': 'ok', "results": {"uid": uid, "receivers": receivers, "strategies": strategies}})


@api.route('/check_wx_bind', methods=['GET'])
def check_wx_bind():
    user = g.user
    wechat = user.wechat
    if wechat:
        msg = 'success'
    else:
        user = g.user
        data = {
            "url": "/accounts/scanv/check_wx_bind/",
            "body": {"username": user.username}
        }
        flag, resp = request_cas(data)
        if not flag:
            msg = "fail"
        elif wx_openid := resp.get("wx_openid"):
            user.update(wechat={"wx_openid": wx_openid})
            msg = 'success'
        else:
            msg = "fail"
    ctx = {
        'code': 200,
        'msg': msg,
    }
    return jsonify(ctx)


@api.route('/receivers', methods=['GET'])
def alert_receivers_list():
    """告警联系人查询"""
    form = AlertReceiversListForm().validate_for_api()
    page = form.page.data
    limit = form.limit.data
    uid = form.user.id
    query_dict = {"uid": uid}
    all_receivers = AlertReceivers.objects.filter(**query_dict)
    total = all_receivers.count()
    page_obj = Pagination(all_receivers, page=page, per_page=limit, not_err=True)
    result = []
    for receiver in page_obj.items:
        item = {
            "receiver_id": str(receiver.pk),
            "email": receiver.email or "",
            "email_verified": receiver.emailVerified,
            "enable": receiver.enable,
            "is_default": receiver.isDefault,
            "name": receiver.name,
            "phone": receiver.phone or "",
            "phone_verified": receiver.phoneVerified,
            "uid": str(receiver.uid),
        }
        result.append(item)

    return jsonify({'code': 200, "status": "success", "msg": "ok",
                    'result': {"data": result, "page": page, "limit": limit, "total": total}})


@api.route('/receiver', methods=['POST'])
def add_receiver():
    """创建告警联系人"""
    form = AddReceiverForm().validate_for_api()
    form.validate_params()
    name = form.name.data
    email = form.email.data
    phone = form.phone.data
    email_code = form.email_code.data
    phone_code = form.phone_code.data
    uid = form.user.id
    receiver = AlertReceivers.objects.filter(uid=uid, name=name).first()
    if receiver:
        raise ParameterError(msg='告警人名称已经存在')

    email_verified = False
    phone_verified = False
    if phone:
        phone_verified = CodeVerified().phone_verified(form.user.id, phone, phone_code)
    if email:
        email_verified = CodeVerified().email_verified(form.user.id, email, email_code)

    balance = getattr(form, "balance", None)
    if not balance:
        raise ParameterError(msg='请升级套餐')
    if balance.balance.alertReceiver.used >= balance.balance.alertReceiver.total:
        raise ParameterError(msg='您还可以添加0个告警联系人')
    alert_receivers = AlertReceivers()
    alert_receivers.name = name
    alert_receivers.enable = True
    alert_receivers.uid = uid
    if phone:
        alert_receivers.phone = phone
        alert_receivers.phoneVerified = phone_verified
    if email:
        alert_receivers.email = email
        alert_receivers.emailVerified = email_verified
    alert_receivers.save()
    balance.update(inc__balance__alertReceiver__used=1)
    detail = f'新增告警联系人, 名称: {name}'
    if phone:
        detail += f", 电话: {phone}"
    if email:
        detail += f", 邮箱: {email}"

    log_info = {
        'message': '新建告警人',
        'actionMessage': '新建告警人',
        'detail': detail,
    }
    CAlertReceiversLog(**log_info).save()
    return jsonify({'code': 200, 'msg': '添加成功', 'status': 'success'})


@api.route('/receiver', methods=['PUT'])
def edit_receiver():
    """修改告警联系人"""
    form = EditReceiverForm().validate_for_api()
    receiver_id = form.receiver_id.data
    name = form.name.data
    email = form.email.data
    phone = form.phone.data
    email_code = form.email_code.data
    phone_code = form.phone_code.data
    uid = form.user.id

    alert_receivers = AlertReceivers.objects.filter(pk=receiver_id, uid=uid).first()
    if not alert_receivers:
        raise ParameterError(msg='告警联系人不存在')

    receiver_name_exist = AlertReceivers.objects.filter(pk__ne=receiver_id, uid=uid, name=name).first()
    if receiver_name_exist:
        raise ParameterError(msg='告警联系人名称已经存在')

    if (name == alert_receivers.name and phone == alert_receivers.phone and email == alert_receivers.email) \
            and not any([email_code, phone_code]):
        return jsonify({'code': 200, 'msg': '没有做任何修改', 'status': 'success'})

    email_verified = alert_receivers.emailVerified
    phone_verified = alert_receivers.phoneVerified
    if phone and (phone != alert_receivers.phone):
        if not phone_code:
            raise ParameterError(msg='请填写手机验证码')
        phone_verified = CodeVerified().phone_verified(form.user.id, phone, phone_code)
    elif phone and phone == alert_receivers.phone and phone_code:
        phone_verified = CodeVerified().phone_verified(form.user.id, phone, phone_code)

    if email and email != alert_receivers.email:
        if not email_code:
            raise ParameterError(msg='请填写邮件验证码')
        email_verified = CodeVerified().email_verified(form.user.id, email, email_code)
    elif email and email == alert_receivers.email and email_code:
        email_verified = CodeVerified().email_verified(form.user.id, email, email_code)

    log_detail = f"修改告警联系人({alert_receivers.name}),"
    if alert_receivers.name != name:
        log_detail += f"名称: {alert_receivers.name} -> {name},"
        alert_receivers.name = name
    if phone != alert_receivers.phone:
        log_detail += f"电话: {alert_receivers.phone} -> {phone}, "
        alert_receivers.phone = phone
    alert_receivers.phoneVerified = phone_verified
    if email != alert_receivers.email:
        log_detail += f"邮箱: {alert_receivers.email} -> {email}, "
        alert_receivers.email = email
    alert_receivers.emailVerified = email_verified
    alert_receivers.save()

    log_info = {
        'affects': [],
        'actionMessage': '编辑告警人',
        'detail': log_detail,
        'message': '编辑告警人'
    }
    UAlertReceiversLog(**log_info).save()
    return jsonify({'code': 200, 'msg': '修改成功', 'status': 'success'})


@api.route('/receiver', methods=['DELETE'])
def delete_receiver():
    form = DeleteReceiverForm().validate_for_api()
    receiver_ids = form.receiver_ids
    uid = form.user.id
    # 默认告警联系人不能删除
    if AlertReceivers.objects.filter(pk__in=receiver_ids, uid=uid, isDefault=True):
        raise ParameterError(msg='默认告警联系人不能删除')
    alert_receivers = AlertReceivers.objects.filter(pk__in=receiver_ids, uid=uid)
    if not alert_receivers:
        raise ParameterError(msg='所选告警联系人不存在')
    name_list = [receiver.name for receiver in alert_receivers]
    AlertReceivers.objects.filter(pk__in=receiver_ids, uid=uid).delete()
    form.balance.update(inc__balance__alertReceiver__used=-len(name_list))
    detail = f'删除告警联系人: {name_list}'
    log_info = {
        'message': '删除告警联系人',
        'actionMessage': '删除告警人',
        'detail': detail,
    }
    DAlertReceiversLog(**log_info).save()
    return jsonify({'code': 200, 'msg': f'删除告警联系人{len(name_list)}个', 'status': 'success'})


@api.route('/receiver/sms_verified_code', methods=['GET'])
def receiver_sms_verified():
    """短信验证码"""
    form = ReceiverVerifiedForm().validate_for_api()
    phone = form.phone.data
    if not phone:
        raise ParameterError(msg='请输入正确的手机号码')
    # 生成sms code
    sms_code = "%06d" % random.randint(0, 999999)
    app.logger.debug(f'sms code: {sms_code}, phone: {phone}')
    yunpian = YunPian()
    args = f'#code#={sms_code}'
    CodeVerified.check_code_time(form.user.id, phone)
    try:
        yunpian.tpl_send(phone, 5238810, args)
    except Exception as e:
        app.logger.exception(f'sms code: {sms_code}, phone: {phone}, error: {str(e)}')
        raise ParameterError(msg='发送失败')
    CodeVerified.save_code(form.user.id, phone, sms_code)
    return jsonify({'code': 200, 'msg': '发送成功', 'status': 'success'})


@api.route('/receiver/email_verified_code', methods=['GET'])
def receiver_email_verified():
    """邮箱验证码"""
    form = ReceiverVerifiedForm().validate_for_api()
    email = form.email.data
    if not email:
        raise ParameterError(msg='请输入正确的邮箱')
    CodeVerified.check_code_time(form.user.id, email)
    # 生成sms code
    email_code = "%06d" % random.randint(0, 999999)
    app.logger.debug(f'email code: {email_code}, email: {email}')
    send_cloud = SendCloud()
    args = {'%code%': [email_code], "%user%": [email]}
    args = {
        'to': [email],
        'sub': args
    }
    try:
        send_cloud.sendmail("scanv_add_receiver", args)
    except Exception as e:
        app.logger.exception(f'email code: {email_code}, phone: {email}, error: {str(e)}')
        raise ParameterError(msg='发送失败')

    CodeVerified.save_code(form.user.id, email, email_code)
    return jsonify({'code': 200, 'msg': '发送成功', 'status': 'success'})
